package net.xiaohanlin.smartmetric.monitor.system;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hyperic.sigar.FileSystemUsage;
import org.hyperic.sigar.SigarException;

import com.codahale.metrics.Gauge;
import com.codahale.metrics.Metric;
import com.codahale.metrics.MetricSet;

import net.xiaohanlin.smartmetric.metricname.DiskMetricNameEnum;

public class DiskGaugeSet extends AbstractSystemMetric implements MetricSet {

	private volatile long lastStatTime = -1L;

	private long lastMetricsReadUnit = 0;
	private long lastMetricsWriteUnit = 0;
	private long lastMetricsTotalRead = 0;
	private long lastMetricsToatlWrite = 0;

	protected long statTimeUnit = 60 * 1000L;

	public enum FSType {
		// Ordered so that ordinals match the constants
		// in org.hyperic.sigar.FileSystem
		Unknown, None, LocalDisk, Network, Ramdisk, Cdrom, Swap
	}

	protected void stat() {
		long passedTime = System.currentTimeMillis() - lastStatTime;
		if (passedTime > statTimeUnit) {
			long totalWrite = 0, totalRead = 0;
			for (MyFileSystem fileSystem : getFilesystems()) {
				totalWrite += fileSystem.write();
				totalRead += fileSystem.read();
			}
			if (passedTime / statTimeUnit > 2) {// 超过两分钟需要重新计算平均值
				int unitCount = (int) (passedTime / statTimeUnit);
				lastMetricsReadUnit = (totalRead - lastMetricsTotalRead) / unitCount;
				lastMetricsWriteUnit = (totalWrite - lastMetricsToatlWrite) / unitCount;

			} else {
				lastMetricsReadUnit = totalRead - lastMetricsTotalRead;
				lastMetricsWriteUnit = totalWrite - lastMetricsToatlWrite;
			}

			lastMetricsTotalRead = totalRead;
			lastMetricsToatlWrite = totalWrite;
		}
	}

	@Override
	public Map<String, Metric> getMetrics() {
		final Map<String, Metric> gauges = new HashMap<String, Metric>();

		gauges.put(getMetricName(DiskMetricNameEnum.disk_read), new Gauge<Long>() {
			public Long getValue() {
				stat();
				return lastMetricsReadUnit;
			}
		});
		
		gauges.put(getMetricName(DiskMetricNameEnum.disk_write), new Gauge<Long>() {
			public Long getValue() {
				stat();
				return lastMetricsWriteUnit;
			}
		});

		gauges.put(getMetricName(DiskMetricNameEnum.disk_total_free), new Gauge<Long>() {
			public Long getValue() {
				long totalFreeSpaceKB = 0;
				for (MyFileSystem fileSystem : getFilesystems()) {
					totalFreeSpaceKB += fileSystem.freeSpaceKB();
				}
				return totalFreeSpaceKB / 1000;
			}
		});

		gauges.put(getMetricName(DiskMetricNameEnum.disk_total_used), new Gauge<Long>() {
			public Long getValue() {
				long totalFreeSpaceKB = 0;
				long totalKB = 0;
				for (MyFileSystem fileSystem : getFilesystems()) {
					totalFreeSpaceKB += fileSystem.freeSpaceKB();
					totalKB += fileSystem.totalSizeKB();
				}
				return (totalKB - totalFreeSpaceKB) / 1000;
			}
		});

		return gauges;
	}

	public List<MyFileSystem> getFilesystems() {
		List<MyFileSystem> result = new ArrayList<MyFileSystem>();
		org.hyperic.sigar.FileSystem[] fss = null;
		try {
			fss = sigar.getFileSystemList();
		} catch (SigarException e) {
			// give up
			return result;
		}

		if (fss == null) {
			return result;
		}

		for (org.hyperic.sigar.FileSystem fs : fss) {
			long totalSizeKB = 0L;
			long freeSpaceKB = 0L;
			long write = 0L;
			long read = 0L;
			try {
				FileSystemUsage usage = sigar.getFileSystemUsage(fs.getDirName());
				totalSizeKB = usage.getTotal();
				freeSpaceKB = usage.getFree();
				write = usage.getDiskWrites();
				read = usage.getDiskReads();
			} catch (SigarException e) {
				// ignore
			}
			result.add(MyFileSystem.create(fs, totalSizeKB, freeSpaceKB, read, write));
		}
		return result;
	}

	public static final class MyFileSystem {

		private final String deviceName;
		private final String mountPoint;
		private final FSType genericFSType;
		private final String osSpecificFSType;
		private final long totalSizeKB;
		private final long freeSpaceKB;
		private final long read;
		private final long write;

		public MyFileSystem( //
				String deviceName, String mountPoint, //
				FSType genericFSType, String osSpecificFSType, //
				long totalSizeKB, long freeSpaceKB, long read, long write) {
			this.deviceName = deviceName;
			this.mountPoint = mountPoint;
			this.genericFSType = genericFSType;
			this.osSpecificFSType = osSpecificFSType;
			this.totalSizeKB = totalSizeKB;
			this.freeSpaceKB = freeSpaceKB;
			this.read = read;
			this.write = write;
		}

		public static MyFileSystem create(org.hyperic.sigar.FileSystem fs, long totalSizeKB, long freeSpaceKB, long read, long write) {
			return new MyFileSystem( //
					fs.getDevName(), fs.getDirName(), //
					FSType.values()[fs.getType()], fs.getSysTypeName(), //
					totalSizeKB, freeSpaceKB, write, read);
		}

		public String deviceName() {
			return deviceName;
		}

		public String mountPoint() {
			return mountPoint;
		}

		public FSType genericFSType() {
			return genericFSType;
		}

		public String osSpecificFSType() {
			return osSpecificFSType;
		}

		public long totalSizeKB() {
			return totalSizeKB;
		}

		public long freeSpaceKB() {
			return freeSpaceKB;
		}

		public long write() {
			return write;
		}

		public long read() {
			return read;
		}
	}

}
